﻿using System;
using System.Diagnostics;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

using SunXNA.RainbowXNA.Material;
using SunXNA.RainbowXNA.Batch;

namespace SunXNA.RainbowXNA.RenderObject
{
    class CAnimatedMeshRenderInstance : CStaticMeshRenderInstance
    {
        private SkinnedModel.SkinningData m_skinningData;
        private SkinnedModel.AnimationClip m_animationClip;
        private SkinnedModel.AnimationPlayer m_animationPlayer;

        public CAnimatedMeshRenderInstance(CRainbowXNA.RenderObjectHandleType handle)
            : base( handle )
        {
            //ObjectHandle = handle;
            //World = Matrix.Identity;

            //m_UID = ++s_LastUID;

            //m_rTimeStamp = -1.0f;
            //m_material = new SunXNA.RainbowXNA.Material.CFlatShadedMaterial();

            //if (m_objectHandle != null)
            //{
            //    m_obb = OBB.CreateFromBox(m_objectHandle.GraphicsBox);
            //}
            //else
            //{
            //    m_obb = null;
            //}
        }

        public SkinnedModel.SkinningData SkinningData
        {
            get
            {
                return m_skinningData;
            }

            set
            {
                m_skinningData = value;
            }
        }

        public SkinnedModel.AnimationClip AnimationClip
        {
            get
            {
                return m_animationClip;
            }

            set
            {
                m_animationClip = value;
                m_animationPlayer.StartClip(m_animationClip);
            }
        }

        public SkinnedModel.AnimationPlayer AnimationPlayer
        {
            get
            {
                return m_animationPlayer;
            }

            set
            {
                m_animationPlayer = value;
            }
        }
    }

    class CAnimatedMeshPartRenderInstance : CRainbowXNA.CRenderInstance
    {
        private CAnimatedMeshRenderInstance m_parent;

        public CAnimatedMeshPartRenderInstance(CRainbowXNA.RenderObjectHandleType handle)
            : base(handle)
        { }

        public CAnimatedMeshRenderInstance Parent
        {
            get
            {
                return m_parent;
            }

            set
            {
                m_parent = value;
            }
        }
    }

    class CAnimatedMeshRenderObject : CStaticMeshRenderObject
    {
        #region Public Types
        #endregion

        #region Private Member Variables

        private SkinnedModel.SkinningData m_skinningData;

        #endregion

        #region Public Accessors
        #endregion

        #region Public Member Functions

        public CAnimatedMeshRenderObject(ContentManager contentManager, string AssetName, CRainbowXNA rainbow)
            : base()
        {

            m_model = contentManager.Load<Model>(AssetName);

            m_skinningData = m_model.Tag as SkinnedModel.SkinningData;

            if (m_skinningData == null)
                throw new InvalidOperationException
                    ("This model does not contain a SkinningData tag.");

            m_graphicsVolume = m_model.Meshes[0].BoundingSphere;
            m_box = BoundingBox.CreateFromSphere(m_model.Meshes[0].BoundingSphere);

            int totalMeshParts = 0;
            foreach (ModelMesh mesh in m_model.Meshes)
            {
                BoundingSphere meshBoundingSphere = mesh.BoundingSphere;
                BoundingBox meshBoundingBox = BoundingBox.CreateFromSphere(m_model.Meshes[0].BoundingSphere);

                BoundingSphere.CreateMerged(ref m_graphicsVolume,
                                                ref meshBoundingSphere,
                                                out m_graphicsVolume);

                BoundingBox.CreateMerged(ref m_box,
                                         ref meshBoundingBox,
                                         out m_box);

                totalMeshParts += mesh.MeshParts.Count;

                Trace.Assert(mesh.MeshParts.Count == mesh.Effects.Count);
            }

            m_meshPartHandles = new CRainbowXNA.RenderObjectHandleType[totalMeshParts];
            m_meshPartDefaultTextureHandles = new CRainbowXNA.TextureObjectHandleType[totalMeshParts];

            int i = 0;
            foreach (ModelMesh mesh in m_model.Meshes)
            {
                foreach (Effect effect in mesh.Effects)
                {
                    m_meshPartDefaultTextureHandles[i] = rainbow.HandleTexture(effect.Parameters["Texture"].GetValueTexture2D());
                }

                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    m_meshPartHandles[i] = rainbow.HandleMeshPart(mesh, part);
                }

                ++i;
            }

            m_boneTransforms = new Matrix[m_model.Bones.Count];
            m_model.CopyAbsoluteBoneTransformsTo(m_boneTransforms);
        }

        public override void Draw(GraphicsDevice device, Matrix m4World, Matrix m4View, Matrix m4Projection)
        {
            //Matrix[] limbPositions = new Matrix[m_model.Bones.Count];
            //m_model.CopyAbsoluteBoneTransformsTo(limbPositions);

            //foreach (ModelMesh mesh in m_model.Meshes)
            //{
            //    foreach (BasicEffect effect in mesh.Effects)
            //    {
            //        effect.EnableDefaultLighting();
            //        effect.Projection = m4Projection;
            //        effect.View = m4View;
            //        effect.World = limbPositions[mesh.ParentBone.Index] * m4World;
            //    }

            //    mesh.Draw();
            //}
        }

        public override void AddToBatch(CBatchManager batchManager, CRainbowXNA.CRenderInstance instance)
        {
            Trace.Assert(instance is CAnimatedMeshRenderInstance);
            CAnimatedMeshRenderInstance modelInstance = (CAnimatedMeshRenderInstance)instance;
            Matrix[] skinTransforms = modelInstance.AnimationPlayer.GetSkinTransforms();

            for (int i = 0; i < modelInstance.MeshPartInstances.Length; ++i)
            {
                CRainbowXNA.CRenderInstance meshPartInstance = modelInstance.MeshPartInstances[i];
                CModelMeshPartRenderObject meshPartRenderObject = (CModelMeshPartRenderObject)meshPartInstance.ObjectHandle.RenderObject;
                meshPartInstance.World = skinTransforms[meshPartRenderObject.Mesh.ParentBone.Index];

                meshPartInstance.ObjectHandle.RenderObject.AddToBatch(batchManager, meshPartInstance);
            }
        }

        public override CRainbowXNA.CRenderInstance CreateDefaultInstance(CRainbowXNA.RenderObjectHandleType handle, CRainbowXNA rainbow)
        {
            //Trace.Assert(handle.RenderObject == this);

            //CStaticMeshRenderInstance renderInstance = new CStaticMeshRenderInstance(handle);

            //renderInstance.MeshPartInstances = new CRainbowXNA.CRenderInstance[m_meshPartHandles.Length];

            //for (int i = 0; i < m_meshPartHandles.Length; ++i)
            //{
            //    renderInstance.MeshPartInstances[i] = rainbow.CreateRenderInstance(m_meshPartHandles[i]);
            //    //m_meshPartHandles[i].RenderObject.CreateDefaultInstance(m_meshPartHandles[i], rainbow);
            //    renderInstance.MeshPartInstances[i].Material = new CTexturedLambertShadedMaterial();

            //    renderInstance.MeshPartInstances[i].Material.Diffuse = Color.White.ToVector4();
            //    renderInstance.MeshPartInstances[i].Material.TextureHandle = m_meshPartDefaultTextureHandles[i];
            //}

            //return renderInstance;

            Trace.Assert(handle.RenderObject == this);

            CAnimatedMeshRenderInstance renderInstance = new CAnimatedMeshRenderInstance(handle);

            renderInstance.MeshPartInstances = new CRainbowXNA.CRenderInstance[m_meshPartHandles.Length];

            renderInstance.SkinningData = m_skinningData;
            renderInstance.AnimationPlayer = new SkinnedModel.AnimationPlayer(m_skinningData);

            IEnumerator<KeyValuePair<string, SkinnedModel.AnimationClip>> enumerator = renderInstance.SkinningData.AnimationClips.GetEnumerator();

            if (!enumerator.MoveNext())
            {
                throw new ArgumentException("renderInstance.SkinningData.AnimationClips");
            }

            renderInstance.AnimationClip = enumerator.Current.Value;

            for (int i = 0; i < m_meshPartHandles.Length; ++i)
            {
                //renderInstance.MeshPartInstances[i] = rainbow.CreateRenderInstance(m_meshPartHandles[i]);
                //m_meshPartHandles[i].RenderObject.CreateDefaultInstance(m_meshPartHandles[i], rainbow);
                renderInstance.MeshPartInstances[i] = new CAnimatedMeshPartRenderInstance(m_meshPartHandles[i]);
                renderInstance.MeshPartInstances[i].Material = new CAnimatedMeshMaterial();
                ((CAnimatedMeshPartRenderInstance)renderInstance.MeshPartInstances[i]).Parent = renderInstance;

                renderInstance.MeshPartInstances[i].Material.Diffuse = Color.White.ToVector4();
                renderInstance.MeshPartInstances[i].Material.TextureHandle = m_meshPartDefaultTextureHandles[i];
            }

            return renderInstance;
        }

        #endregion
    }
}
